iT邦幫忙

2022 iThome 鐵人賽

DAY 27
1
自我挑戰組

學習JavaScript的基礎概念系列 第 27

Day27 Callback、Promise()

  • 分享至 

  • xImage
  •  

Callback

所謂的「Callback function」,簡單來說就是「把函式當作另一個函式的參數,透過另一個函式來呼叫它」。
大部分使用callbacks(回調)函式的是非同步執行,但並非所有都是非同步。
同步的Callback function

  • Array.forEach

非同步的Callback function

  • 使用計時器(timer)函式: setTimeout, setInterval
  • 特殊的函式: nextTick, setImmediate
  • 執行I/O: 監聽網路、資料庫查詢或讀寫外部資源
  • 訂閱事件

範例:

function getData(name, callback) {
    setTimeout(() => {
        callback({
            name:name,
            age:Math.floor(Math.random()*10),
            sex:"male"
        })
    },2000)
}
console.log("start");
getData("Harry",(obj) => {
    console.log(obj);
});
console.log("end");

執行結果:
https://ithelp.ithome.com.tw/upload/images/20221011/20152070qDlbWbTEaN.png

Callback Hell

但過多的Callback function會造成難以閱讀的Callback Hell,一步一步串下去,缺點是閱讀困難。
https://ithelp.ithome.com.tw/upload/images/20221011/201520703XCwLO30Dm.png

Promise()

ES6出現的語法,Promise是一個物件,透過new來建立Promise的建構式。
能表示非同步運算的操作最後是完成或失敗,用resolvereject來決定結果,可應用在連接資料庫時回傳Promise,且不需要再執行Callback,只要拿到它的Promise就好。

成功完成時:執行resolve,回傳Promise物件,作業結果的值被呼叫,取得資料。
執行錯誤時:執行 rejects,Promise會被拒絕,出現失敗訊息。

語法

//executor function,代表一個接收resolve及reject的函式
new Promise( executor function(resolve, reject) { 
        ... ...
    } 
);

executor function:語法出現的executor function,單純代表一個接收resolvereject的函式,接收到resolve及reject後會立刻執行,函式名稱可匿名或自取名。

new來建立Promise的建構式,是同步,會馬上執行。
resolvereject函式,會在被個別呼叫時,個別執行,所以.then()裡面的回呼函式是非同步。且.then() 和 .catch() ,都會把 callback 給 Promise 化。
通常 executor function 會發起一些非同步操作,(例:setTimeoutsetInterval......)。

一個 Promise 物件會有這些狀態:

  • 擱置(pending):初始狀態,不是 fulfilled 與 rejected,不知道到底成功還是失敗的狀態。
  • 實現(fulfilled):表示操作成功。
    使用解決(resolved)一詞來描述promises 被實現(fulfilled)。
  • 拒絕(rejected):表示操作失敗。
  • 解決(settled):一個被實現或拒絕,但不處於 pending 的 promise 被稱作被解決(settled)
    https://ithelp.ithome.com.tw/upload/images/20221011/20152070LgU2Ko788e.png

圖源:Promise - JavaScript | MDN

resolve:裡面是你要的資料,成功時它會把這資料回傳給你,Promise 函式呼叫時使用 .then() 來接收 resolve 的結果,.then() 讓callback可以在非同步運算結束後呼叫

reject:裡面是顯示失敗的訊息,Promise 函式呼叫時使用.cach()來接收reject的結果。

Promise使用resolve的結果:

//用new建立Promise建構式
let example = new Promise((resolve,reject) => {
    resolve({
        name:"Harry",
        age: 20});
});

example.then((d) => {
    console.log(d);
});

執行結果:執行成功,所以顯示resolve裡的資料內容
https://ithelp.ithome.com.tw/upload/images/20221011/20152070drV7mhcm1p.png

使用reject,用.cach接住:

let example =new Promise((resolve,reject) => {
    reject(new Error("not allowed"));
});

example
    .then((d) => {
        console.log(d);
    })
    .catch((e) => {
        console.log(e);
    });

執行結果:出現錯誤訊息
https://ithelp.ithome.com.tw/upload/images/20221011/20152070R5YhCiRmfG.png

用Promise來改寫Callback:

//Callback
function getData(name, callback) {
    setTimeout(() => {
        callback({
            name:name,
            age:Math.floor(Math.random()*10),
            sex:"male"
        })
    },2000)
}
console.log("start");
getData("Harry",(obj) => {
    console.log(obj);
});
console.log("end");

//Promise寫法
function getData2(name) {
    if (name == "Harry") {
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                resolve({
                    name:name,
                    age:Math.floor(Math.random()*10),
                    sex:"male"
                },2000);
            });
        })
    } else {
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                reject(new Error("Not allowed to access data."));
            },2000);
        });
    }
}
console.log("start2");
getData2("Harry2")
    .then((obj) => {console.log(obj);})
    .catch((e) => {console.log(e);})
console.log("end2");

執行結果:給getData2的參數是Harry,不等於Harry2,所以是reject的結果
https://ithelp.ithome.com.tw/upload/images/20221012/201520706EUhIdiZlS.png


參考資料:

使用 Promise - JavaScript | MDN

Promise - JavaScript | MDN

Day19-JavaScript Promise 系列-更多關於 Promise 的練習

回呼常用來延續非同步行動完成後的程式執行:這就叫做非同步回呼

JavaScript Promise 類的實作與逐步解說
回呼函式 - 術語表 | MDN

如何透過錯誤的 await 來造成效能低落

callback hell 與 Promise ,一起來把 setTimeout 封裝成 Promise 吧!

面對 Promise 的思考路徑. Promise… | by realdennis | Medium


上一篇
Day26 API、Fetch()、async/await
下一篇
Day28 來製作ToDoList吧 - 1(附上Font Awesome使用教學)
系列文
學習JavaScript的基礎概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言